﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.IO.Packaging;
using System.Linq;
using System.Runtime.Serialization;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Xaml;
using System.Xml.Serialization;
using CoreClasses;
using LayoutPlanning.Helpers;
using LayoutPlanning.ViewModel;
using Microsoft.Expression.Shapes;
using Microsoft.Win32;
using Mvvm;
using Brushes = System.Windows.Media.Brushes;
using RelayCommand = LayoutPlanning.Helpers.RelayCommand;
using XamlReader = System.Windows.Markup.XamlReader;
using XamlWriter = System.Windows.Markup.XamlWriter;

namespace LayoutPlanning.Model
{
    [Serializable]
    public abstract class GraficalObject : ObservableDragableObject
    {
      
        private static SolidColorBrush _noClashColor = Brushes.Green;
        private static SolidColorBrush _clashColor = Brushes.OrangeRed;
       private static BrushConverter _colorConverter = new BrushConverter();


        private string _background;

     
        private SerializableFrameworkElement _control;
        private SerializableFrameworkElement _controlCopy;

        private string _name = "";

        private string _pointCloudFile;
        [NonSerialized]
        private Geometry _shape = null;
        private double _sizeX = 0;
        private double _sizeY = 0;

        private string _normalColor = "#00FFFFFF";
        private ControlType _controlType = ControlType.Square;

        [NonSerialized]
        private FrameworkElement _controlRef;

        private bool _isClashing;
        private ObservableCollection<SpecialRelation> _specialRelations;


        public GraficalObject()
        {
            if (_normalColor==null)
                _normalColor = _colorConverter.ConvertToInvariantString(Brushes.Transparent);
            Background = NormalColor;
            Position.PropertyChanged += Position_PropertyChanged;
            PropertyChanged += GraficalObject_PropertyChanged;
          
        }

        public String Name
        {
            get { return _name; }
            set
            {
                _name = value;
                RaisePropertyChanged();
            }
        }

        public Point CenterPoint => new Point(Position.X + SizeX/2, Position.Y + SizeY/2);

        public double SizeX
        {
            get { return _sizeX; }
            set
            {
                _sizeX = value;
                RaisePropertyChanged();
            }
        }

        public double SizeY
        {
            get { return _sizeY; }
            set
            {
                _sizeY = value;
                RaisePropertyChanged();
            }
        }

        [XmlIgnore]
        public SolidColorBrush Background
        {
            get { return (SolidColorBrush) _colorConverter.ConvertFrom(_background); }
            set
            {
                if (value == null)
                    _background = _colorConverter.ConvertToString(Brushes.Transparent);
                else
                    _background = _colorConverter.ConvertToString(value);
                RaisePropertyChanged();
            }
        }

        public string NormalColorString
        {
            get
            {
                return _normalColor;
            }
            set
            {
                if (value == _normalColor)
                    return;
                _normalColor = value;
                RaisePropertyChanged();
                RaisePropertyChanged(nameof(NormalColor));
                RaisePropertyChanged(nameof(Background));
            }
        }

        [XmlIgnore]
        public SolidColorBrush NormalColor
        {
            get { return (SolidColorBrush) _colorConverter.ConvertFromString(_normalColor); }
            set
            {
                if (value == null)
                    _normalColor = _colorConverter.ConvertToInvariantString(Brushes.Transparent);


                else
                 _normalColor = _colorConverter.ConvertToInvariantString(value);
            }
        }


        [XmlIgnore]
        public static SolidColorBrush ClashColor
        {
            get { return _clashColor; }
            set { _clashColor = value; }
        }
        [XmlIgnore]
        /// <summary>
        /// 
        /// </summary>
        public Helpers.RelayCommand<GraficalObject> RefreshBackground
        {
            get
            {
                return new Helpers.RelayCommand<GraficalObject>(async (station) =>
                {
                    var stations = await ViewModelLocator.Main.OverlappingStations(station);
                    var rest = ViewModelLocator.Main.Model.DeployedGraficalObjects.Where(t=>t.IsClashing).Except(stations);
                    stations.ForEach((s) => s.SetClash(true));

                    foreach (var s in rest) s.SetClash((await ViewModelLocator.Main.OverlappingStations(s)).Any());

                    if (stations.Count == 0)
                        station.SetClash(false);
                    else
                        station.SetClash(true);
                });
            }
        }

        [XmlIgnore]
        public RelayCommand ResetWorkTaskSelection
        {
            get
            {
                return new RelayCommand(() =>
                {
                    var station = this as Station;
                    if (station == null)
                        return;
                    station.SelectedWorkGripPosition = null;
                });
            }
        }

        public string PointCloudFile
        {
            get { return _pointCloudFile; }
            set
            {
                _pointCloudFile = value;
                RaisePropertyChanged();
            }
        }
        [XmlIgnore]
        public RelayCommand BrowsePointCloudFile
        {
            get
            {
                return new RelayCommand(() =>
                {
                    var dialog = new OpenFileDialog();
                    dialog.AddExtension = true;
                    dialog.Filter = "All (*.*)|*.*";

                    if (dialog.ShowDialog() ?? false)
                    {
                        PointCloudFile = dialog.FileName;
                    }
                });
            }
        }

        public RelayCommand<string> ChangeShape => new RelayCommand<string>(t =>
        {
            switch (t)
            {
                case "Circle":
                    ControlType = ControlType.Circle;
                    break;
                case "Square":
                    ControlType = ControlType.Square;
                    break;
                case "Custom":
                    ControlType = ControlType.Custom;
                    SetCustomControl(CustomControl?.Element);
                    break;

            }
        });
        [XmlIgnore]
        public SerializableFrameworkElement ControlCopy
        {
            get { return _controlCopy; }
            private set
            {
                if (_controlCopy == value)
                    return;
                _controlCopy = value;
                RaisePropertyChanged();
            }
        }


        [XmlIgnore]
        public FrameworkElement Control
        {
            get

            {
                switch (ControlType)
                {
                        case ControlType.Custom:
                            return _control?.Element;
                        case ControlType.Circle:
                        case ControlType.Square:
                        return _controlRef;

                }
                return _control?.Element;

            }
        }

        public void SetCustomControl(ContentControl element)
        {

            if (element != null)
                CustomControl = new SerializableFrameworkElement(element);
            if (element == null)
                return;
            element.DataContext = this;
            RaisePropertyChanged("Control");
            ControlType = ControlType.Custom;
        }

        public ControlType ControlType
        {
            get
            {

                return _controlType;
            }
            set
            {

                if (_controlType == value)
                    return;
                _controlType = value;
                RaisePropertyChanged(nameof(Control));
                RaisePropertyChanged();


            }
        }

        public SerializableFrameworkElement CustomControl
        {
            get { return _control; }
            set
            {
                if(_control == value)
                    return;
                _control = value; 
                ControlType = ControlType.Custom;
                RaisePropertyChanged();
                ViewModelLocator.Dispatcher.InvokeAsync(() => ControlCopy = _control.XamlSyncClone(),
                    DispatcherPriority.ContextIdle);

            }
        }

        public RelayCommand<FrameworkElement> VisibleChanged =>  new RelayCommand<FrameworkElement>(t =>
        {
            if(t.IsVisible)
                _controlRef = t;
        });
       


        private void GraficalObject_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName == "Position")
                Position.PropertyChanged += Position_PropertyChanged;
        }

        private void Position_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            RaisePropertyChanged(nameof(CenterPoint));
        }


        public abstract Task<GraficalObject> Copy();


        public void SetClash(bool isClashing)
        {
            _isClashing = isClashing;
            Background = isClashing ? ClashColor : NormalColor;
        }

        public bool IsClashing
        {
            get { return _isClashing; }
            private set
            {
                if(_isClashing == value)
                    return;
                _isClashing = value; 
                RaisePropertyChanged();
            }
        }

        public ObservableCollection<SpecialRelation> SpecialRelations
        {
            get { return _specialRelations; }
            set
            {
                _specialRelations = value;
                RaisePropertyChanged();
            }
        } 

        public RelayCommand AddSpecialRelation => new RelayCommand(() =>
        {
            if(SpecialRelations == null)
                SpecialRelations = new ObservableCollection<SpecialRelation>();
            SpecialRelations.Add(new SpecialRelation() {TypeOfRelation = RelationType.TryToKeepClose, RelationTo = ViewModelLocator.Main?.DeployedStations?.FirstOrDefault()});
        });
        
    }
    [Serializable]
    public class SpecialRelation: BindableBase, ISerializable
    {
        [NonSerialized]
        private GraficalObject _relationTo;

        private string _relationToString;
        private RelationType _typeOfRelation;

        public SpecialRelation()
        {
        }

        protected SpecialRelation(SerializationInfo info, StreamingContext context)
        {
            _relationToString = (string)info.GetValue("RelationToString", typeof(string));
            _relationTo = ViewModelLocator.Main.DeployedStations.FirstOrDefault(t => t.Name == _relationToString);
            _typeOfRelation = (RelationType)info.GetValue("TypeOfRelation", typeof(RelationType));
            
        }

        public GraficalObject RelationTo
        {
            get
            {
                if(_relationTo == null)
                     _relationTo=  ViewModelLocator.Main.DeployedStations.FirstOrDefault(t => t.Name == _relationToString);
                return _relationTo;
            }
            set 
            {
                if(value == null || value == _relationTo)
                    return;
                _relationTo = value;
                _relationToString = _relationTo?.Name;
                RaisePropertyChanged();
            }
        }

        public RelationType TypeOfRelation
        {
            get { return _typeOfRelation; }
            set
            {
                _typeOfRelation = value;
                RaisePropertyChanged();
            }
        }

        [XmlIgnore]
        public RelayCommand<GraficalObject> Delete  => new RelayCommand<GraficalObject>((t)=> t.SpecialRelations.Remove(this)  );
        

        public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {

            info.AddValue("RelationToString", _relationToString);

            info.AddValue("TypeOfRelation", _typeOfRelation);


        }
    }

    [Serializable]
    public enum RelationType
    {
        [Description("Must Be immediate Next To")]
        MustBeImmediateNextTo = 1000,
        [Description("Try To Keep Close")]
        TryToKeepClose = 1,
        [Description("Try To Keep Away")]
        TryToKeepAway = -1,
        [Description("As Far Away As Possible")]
        AsFarAwayAsPossible = -1000

    }
    [Serializable]
    public enum ControlType 
    {
        Custom, Circle, Square
    }
}